// 是否为url地址
export const isURL = (s: string) => /^http[s]?:\/\/.*/.test(s);
/**
 * 判断是否为URLSearchParams，来自axios源码
 * Determine if a value is a URLSearchParams object
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a URLSearchParams object, otherwise false
 */
export const isURLSearchParams = (
	val: Record<string, any>
): val is URLSearchParams => {
	return (
		typeof URLSearchParams !== "undefined" && val instanceof URLSearchParams
	);
};
// 是否为图片Base64地址
export const isImgBase64 = (s: string) => /^data:image?/.test(s);

// 邮箱验证
export const isEmail = (s: string) => {
	return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(
		s
	);
};
// 手机号验证
export const isMobile = (s: string) => {
	return /^1[0-9]{10}$/.test(s);
};
// 座机号码验证
export const isTel = (s: string) => {
	return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(s);
};
// 身份证号码验证
export const isCard = (s: string) => {
	return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(s);
};
// 严格的身份证号码验证
export const isCardID = (sId: string) => {
	if (!/(^\d{15}$)|(^\d{17}(\d|X|x)$)/.test(sId)) {
		console.log("你输入的身份证长度或格式错误");
		return false;
	}
	//身份证城市
	let aCity: Record<number, string> = {
		11: "北京",
		12: "天津",
		13: "河北",
		14: "山西",
		15: "内蒙古",
		21: "辽宁",
		22: "吉林",
		23: "黑龙江",
		31: "上海",
		32: "江苏",
		33: "浙江",
		34: "安徽",
		35: "福建",
		36: "江西",
		37: "山东",
		41: "河南",
		42: "湖北",
		43: "湖南",
		44: "广东",
		45: "广西",
		46: "海南",
		50: "重庆",
		51: "四川",
		52: "贵州",
		53: "云南",
		54: "西藏",
		61: "陕西",
		62: "甘肃",
		63: "青海",
		64: "宁夏",
		65: "新疆",
		71: "台湾",
		81: "香港",
		82: "澳门",
		91: "国外",
	};
	if (!aCity[parseInt(sId.substr(0, 2))]) {
		console.log("你的身份证地区非法");
		return false;
	}
	// 出生日期验证
	let sBirthday = (
		sId.substr(6, 4) +
		"-" +
		Number(sId.substr(10, 2)) +
		"-" +
		Number(sId.substr(12, 2))
	).replace(/-/g, "/"),
		d = new Date(sBirthday);
	if (
		sBirthday !=
		d.getFullYear() + "/" + (d.getMonth() + 1) + "/" + d.getDate()
	) {
		console.log("身份证上的出生日期非法");
		return false;
	}
	// 身份证号码校验
	let sum = 0,
		weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2],
		codes = "10X98765432";
	for (let i = 0; i < sId.length - 1; i++) {
		sum += Number(sId[i]) * weights[i];
	}
	let last = codes[sum % 11]; //计算出来的最后一位身份证号码
	if (sId[sId.length - 1] != last) {
		console.log("你输入的身份证号非法");
		return false;
	}
	return true;
};

// 以字母开头，长度在6~18之间，只能包含字母、数字和下划线的密码验证
export const ispwd = (s: string) => {
	return /^[a-zA-Z]\w{5,17}$/.test(s);
};
// 检测密码强度
export const checkPwd = (str: string) => {
	let Lv = 0;
	if (str.length < 6) {
		return Lv;
	}
	if (/[0-9]/.test(str)) {
		Lv++;
	}
	if (/[a-z]/.test(str)) {
		Lv++;
	}
	if (/[A-Z]/.test(str)) {
		Lv++;
	}
	if (/[\.|-|_]/.test(str)) {
		Lv++;
	}
	return Lv;
};

// 通过Object.prototype.toString精准判断数据类型
const verifyType = (o: unknown, type: string) =>
	Object.prototype.toString.call(o).slice(8, -1) === type;
// 判断是否为字符串
export const isString = (o: unknown): o is string => verifyType(o, "String");
// 判断是否为数字
export const isNumber = (o: unknown): o is number => verifyType(o, "Number");
// 判断是否为boolean
export const isBoolean = (o: unknown): o is boolean => verifyType(o, "Boolean");
// 判断是否为File对象
export const isFile = (o: unknown): o is File =>
	verifyType(o, "File");
// 判断是否为函数
export const isFunction = (o: unknown): o is Function =>
	verifyType(o, "Function");
// 判断是否为null
export const isNull = (o: unknown): o is null => verifyType(o, "Null");
// 判断是否为undefined
export const isUndefined = (o: unknown): o is undefined =>
	verifyType(o, "Undefined");
// 判断是否为对象

export const isObj = (o: unknown): o is Record<any, any> =>
	verifyType(o, "Object");

// 判断是否为json
export const isJson = (o: string): o is string => {
	if (typeof o === "string") {
		try {
			const obj = JSON.parse(o);
			return !!(typeof obj === "object" && obj);
		} catch (e) {
			return false;
		}
	}
	return false;
};
// 判断是否为数组

export const isArray = Array.isArray;
// 判断是否为时间
export const isDate = (o: unknown): o is Date => verifyType(o, "Date");
// 判断是否为正则
export const isRegExp = (o: unknown): o is RegExp => verifyType(o, "RegExp");
// 判断是否为错误对象
export const isError = (o: unknown): o is Error => verifyType(o, "Error");
// 判断是否为Symbol函数
export const isSymbol = (o: unknown): o is symbol => verifyType(o, "Symbol");
// 判断是否为Promise对象
export const isPromise = <T = any>(o: unknown): o is Promise<T> =>
	verifyType(o, "Promise");
// 判断是否为Set对象
export const isSet = <T = any>(o: unknown): o is Set<T> => verifyType(o, "Set");
// 判断是否为Map对象
export const isMap = <K = any, T = any>(o: unknown): o is Map<K, T> =>
	verifyType(o, "Map");
// 判断是否为图片
export const isImage = (value: string) => {
	let regExp = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i;
	return regExp.test(value);
};
// 判断是否为视频
export const isVideo = (value: string) => {
	let regExp = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv)/i;
	return regExp.test(value);
};
export const isEqual = (obj1: unknown, obj2: unknown) => {
	// 两个数据有任何一个不是对象或数组,则直接判断值是否严等于
	if (!isObj(obj1) || !isObj(obj2) || !isArray(obj1) || !isArray(obj2)) {
		return obj1 === obj2
	}
	// 如果传的两个参数都是同一个对象或数组
	if (obj1 === obj2) {
		return true
	}

	// 两个都是对象或数组，而且不相等
	//比较obj1和obj2的key的个数，是否一样
	const obj1Keys = Object.keys(obj1)
	const obj2Keys = Object.keys(obj2)
	if (obj1Keys.length !== obj2Keys.length) {
		return false
	}

	// 如果key的个数相等,则递归比较
	for (let key in obj1) {
		const res = isEqual(obj1[key], obj2[key])
		if (!res) {
			return false
		}
	}

	return true
}

// 判断一个值是否为空
export const empty = (val: unknown) => {
	return (
		(!val && val !== 0) ||
		(isArray(val) && val.length === 0) ||
		(isObj(val) && !Object.keys(val).length)
	);
};

// 判断一个值是否为数字
export const testNumber = (value?: string | number) => {
	return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(`${value}`);
};

export const hasOwnProperty = Object.prototype.hasOwnProperty;
// 判断一个属性是否为传入的对象的自有属性，源码来自@vue/shared 详见：https://www.npmjs.com/package/@vue/shared
export const hasOwn = (val: object, key: string | symbol): key is never =>
	hasOwnProperty.call(val, key);

// 判断是否为车牌号
export const carNo = (s: string) => {
	// 新能源车牌
	const xreg =
		/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/;
	// 旧车牌
	const creg =
		/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/;
	if (s.length === 7) {
		return creg.test(s);
	}
	if (s.length === 8) {
		return xreg.test(s);
	}
	return false;
};
// 判断字符串是否仅包含字母
export const letter = (s: string) => {
	return /^[a-zA-Z]*$/.test(s);
};

// 判断字符串是否仅包含字母与数字
export const enOrNum = (s: string) => {
	const reg = /^[0-9a-zA-Z]*$/g;
	return reg.test(s);
};
// 判断是否为html元素
export const isHtmlElement = (node: { nodeType: number }) => {
	return node && node.nodeType === Node.ELEMENT_NODE;
};


/**
 * 验证一个值是否在给定的范围内[min, max]
 *
 * @param {number|string} val
 * @param {[number|string,number|string]} param 
 * @returns {boolean} 
 */
export const checkNumberRange = (value: number | string, arr: [number | string, number | string]): boolean => {
	if (empty(value)) return false
	return value >= arr[0] && value <= arr[1]
}